package com.etnet.streamserver.view.client;

import com.etnet.streamserver.view.Configure;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NssClient extends Thread {
    private Logger log = LoggerFactory.getLogger(super.getClass());
    ExecutorService executorService;
    boolean isConnected = false;
    boolean isShutdown = false;
    private Lock unFairLock = new ReentrantLock();
    private String serverip;
    private String serverport;
    private Socket socket = null;
    private DataInputStream in;
    private DataOutputStream out;
    private long interval = 15000L;
    private int count = 0;
    private ClientManager client;

    public NssClient(String ip, String port,ClientManager client) {
        this.serverip = ip;
        this.serverport = port;
        this.client = client;
    }

    private boolean connect() {
        boolean ret = false;
        this.unFairLock.lock();
        try {
            if (this.socket == null) {
                int port = Integer.parseInt(this.serverport);
                this.socket = new Socket(this.serverip, port);
                this.socket.setTcpNoDelay(true);
                this.socket.setSoLinger(true, 1);
                this.socket.setSoTimeout(0);
                this.socket.setKeepAlive(false);
                this.socket.setReceiveBufferSize(10240);
                ret = true;
                this.log.info("[connect to server ok port]: " + this.socket.getLocalPort());
                this.isConnected = true;
            }
        } catch (Exception ex) {
            ret = false;
            this.log.info("[connect to server error]:\r\n" + ex.getMessage());
        } finally {
            this.unFairLock.unlock();
        }
        return ret;
    }

    public synchronized int getCount() {
        return this.count;
    }

    public synchronized void setCount(int count) {
        this.count = count;
    }

    public void sendMessage(String msg) throws IOException {
        this.out.write(msg.getBytes());
        this.out.flush();
    }

    private String readHeader(InputStream is) {
        ByteArrayOutputStream buff = new ByteArrayOutputStream();
        try {
            while (!this.isShutdown) {
                byte b = (byte) is.read();
                if (b != -1) {
                    buff.write(b);
                    if (b == 10)
                        return new String(buff.toByteArray(), "UTF-8");
                }
            }
        } catch (Exception e) {
            this.log.error("ReadHeader exception " + e.getMessage());
            return null;
        }
        return null;
    }

    private String readBody(DataInputStream is, int limit) throws Exception {
        String ret = null;
        byte[] buff = new byte[limit];
        is.readFully(buff);
        ret = new String(buff, "utf-8");
        return ret;
    }

    public void close() {
        this.isConnected = false;
        this.isShutdown = true;
    }

    public void run() {
        CheckSystemHeartbeat heartbeat = new CheckSystemHeartbeat(this);
        heartbeat.start();
        while (!this.isShutdown){
            try {
                if (this.socket == null) {
                    if (!connect()) {
                        isConnected = false;
                        setCount(0);
                        Thread.sleep(interval);
                        continue;
                    } else {
                        this.in = new DataInputStream(this.socket.getInputStream());
                        this.out = new DataOutputStream(this.socket.getOutputStream());
                        String sendLoginStr = NssSenderManager.sendLoginStr(Configure.getToken());
                        sendMessage(sendLoginStr);
                        setCount(0);
                    }
                }

                while (!this.isShutdown) {
                    // if (!checkHeartBeat()) {
                    // throw new RuntimeException("heartbeat count more than three!");
                    // }
                    String header = null;
                    String content = null;

                    header = readHeader(this.in);
                    String[] headers = header.split(",");
                    int len = Integer.parseInt(headers[0]);
                    content = readBody(this.in, len);
                    if (headers[1].equals("0")) {
                        setCount(0);
                        this.log.info("heartbeat:" + content);
                    } else if (headers[1].equals("8")) {
                        if (content.trim().equals("0")) {
                            this.log.info("login to server sucess");
                        } else if (content.trim().equals("1")) {
                            this.log.info("login to server fail");
                            Thread.sleep(this.interval);
                        }
                    } else {                        
                        String reqid = headers[1];
                        ResultData rsData = new ResultData();
                        rsData.setContent(content);
                        rsData.setHeader(header);
                        this.client.putResult(reqid, rsData);
                        String[] temp = content.split("\n");
                        this.log.warn("header:" + headers[1]);
                        for (int i = 0; i < temp.length; ++i)
                            this.log.warn("row:" + temp[i]);
                    }
                }
            } catch (RuntimeException e) {
                this.log.error(e.getMessage(), e);
            } catch (Exception e) {
                this.log.error("socket is error! ", e);
            } finally {
                try {
                    this.socket = null;
                    this.in.close();
                    this.in = null;
                    this.out.close();
                    this.out = null;
                    setCount(0);
                } catch (Exception localException4) {
                }
            }
        }
    }

    private boolean checkHeartBeat() {
        boolean ret = false;
        if (getCount() >= 0)
            ret = true;
        else if ((getCount() == -1) || (getCount() == -2))
            ret = true;
        else if (getCount() <= -3) {
            ret = false;
        }
        return ret;
    }

    class CheckSystemHeartbeat extends Thread {
        private NssClient client;

        public CheckSystemHeartbeat(NssClient client) {
            this.client = client;
        }

        public void run() {
            while (!NssClient.this.isShutdown)
                try {
                    Thread.sleep(NssClient.this.interval);
                    int k = NssClient.this.getCount() - 1;
                    NssClient.this.setCount(k);
                    if (!checkHeartBeat()) {
                        try {
                            socket = null;
                            in.close();
                            in = null;
                            out.close();
                            out = null;
                            setCount(0);
                            log.warn("heartbeat count more than three!");
                        } catch (Exception ep) {
                        }
                    }
                } catch (Exception e) {
                    NssClient.this.log.error("heartbeat is error!", e);
                }
        }
    }
}